package prh25.biblioteca.service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

import prh25.biblioteca.entity.Arquivo;
import prh25.biblioteca.entity.Documento;
import prh25.biblioteca.entity.Usuario;
import prh25.biblioteca.entity.dao.ArquivoDAO;
import prh25.biblioteca.entity.dao.DocumentoDAO;
import prh25.biblioteca.entity.dao.UsuarioDAO;
import prh25.biblioteca.entity.dao.hibernate.HibernateDAOFactory;
import prh25.biblioteca.entity.dao.hibernate.HibernateUtil;
import prh25.biblioteca.service.exception.DocumentoServiceException;
import prh25.biblioteca.service.exception.UsuarioServiceException;








public class DocumentoServiceImpl implements DocumentoService {

	
	
	
	
	
	DocumentoDAO documentoDAO;

	public DocumentoDAO getDocumentoDAO() {
		return documentoDAO;
	}

	public void setDocumentoDAO(DocumentoDAO documentoDAO) {
		this.documentoDAO = documentoDAO;
	}

	@Override
	/**
	 * Salva um arquivo no banco de dados.
	 * @param documento - Documento a ser salvo no banco de dados.
	 * @param diretorioAplicacao - diretorio onde se encontra o arquivo.
	 * @return True se o arquivo foi salvo com sucesso, false caso contrario.
	 */
	public boolean finalizarEnvioArquivo(Documento documento, String diretorioAplicacao) {
		// TODO Auto-generated method stub
		//File arquivo2 = new File(".");
		//if(true){
		//throw new DocumentoServiceException(arquivo2.getAbsolutePath());
		//}
		
		File arquivo = new File(diretorioAplicacao + File.separator + "uploadbib"
				+ File.separator
				+ documento.getSource());
		
		if (!arquivo.exists()) {
			throw new DocumentoServiceException("O arquivo "
					+ arquivo.getAbsolutePath()
					+ " nao foi enviado corretamente para o servidor");
		}

		Arquivo arquivoDoc = new Arquivo();
		arquivoDoc.setConteudo(getBytesFromFile(arquivo));
		
		documento.setArquivo(arquivoDoc);
		
		ArquivoDAO arquivoDAO = HibernateDAOFactory.DEFAULT.buildArquivoDAO();

		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		
		try {
			HibernateUtil.beginTransaction();
			arquivoDAO.saveOrUpdate(arquivoDoc);
			documentoDAO.saveOrUpdate(documento);
			HibernateUtil.commitTransaction();
			
		} catch (Exception ex) {
			ex.printStackTrace();
			HibernateUtil.rollbackTransaction();
			
			throw new DocumentoServiceException(
					"Nao foi possivel salvar o documento. \n" +
					"Tente enviar o arquivo novamente.");
		}finally{
			HibernateUtil.getSession().flush();
			HibernateUtil.getSession().clear();
		}
		
		return apagarArquivo(arquivo);	
	}
   
	/**
	 * Apaga o conteudo do arquivo.
	 * @param file - Arquivo que tera o conteudo apagado.
	 * @return True se o arquivo foi apagado com sucesso, falso
	 * caso contrario.
	 */
	private static boolean apagarArquivo(File file){
		 return file.delete();
	 }
	 
	 
	 
	 
	private static boolean gravaBytesFormFile(File destino, byte[] dados){
		
		try {
			if(destino.exists()){
				destino.delete();
			}else{
				destino.createNewFile();
			}
			
			FileOutputStream o = new FileOutputStream(destino);
			 BufferedOutputStream out = new BufferedOutputStream(o);
            for(int i= 0 ; i < dados.length; i++){
            	out.write(dados[i]);
            }
			
            out.close();

			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new DocumentoServiceException(
					"Arquivo nao recuperado corretamente.\n" +
					"ErroCode: 1.");
		} catch (IOException e) {
			e.printStackTrace();
			throw new DocumentoServiceException(
					"Arquivo nao recuperado corretamente.\n" +
					"ErroCode: 2.");
		}
		
		return true;
	}
	
	/**
	 * Transforma os dados do arquivo em bytes.
	 * @param file - Arquivo a ser transformado em bytes.
	 * @return O conteudo do arquivo no formato de bytes.
	 */
	private static byte[] getBytesFromFile(File file) {
		

		long length = file.length();

		// Cria um array de bytes para armazenamento
		byte[] bytes = new byte[(int) length];

		// Ler e armazena em bytes
		int offset = 0;
		int numRead = 0;
		try {
			InputStream is = new FileInputStream(file);
			
			while (offset < bytes.length
					&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
				offset += numRead;
			}

			// Verifica se todos os bytes do arquivo foram lidos
			if (offset < bytes.length) {
				throw new DocumentoServiceException(
						"Nao foi possivel completar a leitura total do arquivo.\n"
								+ "Envie o arquivo novamente." + file.getName());
			}

			// Fecha o ImputStream e retorna os bytes

			is.close();
		} catch (IOException e) {
			throw new DocumentoServiceException(
					"Nao foi possivel completar a leitura total do arquivo.\n"
							+ "Envie o arquivo novamente." + file.getName());
		}
		return bytes;
	}

	@Override
	/**
	 * Busca todos os documentos no banco de dados que obedecam ao criterio
	 * de pesquisa passado pelo usuario.
	 * @param pesquisa - Criterio de pesquisa.
	 */
	public List<Documento> listarDocumentos(String pesquisa) {
		/*
		ServletContext servletContext = ;
		String teste = servletContext.getRealPath("");
		teste = teste + "opa";
		File file = new File(teste);
		file.mkdir();
		
		
		*/
		
		
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		List<Documento> documentosBd = new LinkedList<Documento>();
		
		if(pesquisa.equalsIgnoreCase("ativos")){
			documentosBd = documentoDAO.findByAtivo(true);
		}else if(pesquisa.equalsIgnoreCase("bloqueados")){
			documentosBd = documentoDAO.findByAtivo(false);
		}else{
			documentosBd = documentoDAO.findAll();
		}
		
		return documentosBd;
	}
	
	/*
	private List<Documento> limpaConteudo(List<Documento> listaDocumentos){
		List<Documento> documentosVazios = new LinkedList<Documento>();
		for(Documento documento:listaDocumentos){
			Documento doc = documento.
		}
	}
	*/

	@Override
	/**
	 * Deleta um arquivo do banco de dados.
	 * @param uid - O identificador unico do arquivo.
	 */
	public boolean excluirDocumento(String uid) {
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
        List<Documento> documentoBd = documentoDAO.findByEntityUid(uid);
        
        
		
		
		if(documentoBd.isEmpty()){
			throw new DocumentoServiceException("Documento nao Existe");
		}
		
		Documento documento = documentoBd.get(0);
		
		try {
			HibernateUtil.beginTransaction();
			documentoDAO.delete(documento);
			HibernateUtil.commitTransaction();
		} catch (Exception ex) {	
			ex.printStackTrace();
			HibernateUtil.rollbackTransaction();
			throw new DocumentoServiceException("Nao foi possivel remover o documento");
		}finally{
			HibernateUtil.getSession().flush();
			HibernateUtil.getSession().clear();
			
		}
		return true;
	}

	@Override
	/**
	 * Altera o status de um documento, ou seja, se o documento foi ou nao aceito
	 * @param uid - O identificador unico do documento.
	 * @param situacao - O status atual que o documento se encontra no sistema.
	 * @return O documento que tera o seu status alterado. 
	 */
	public Documento setSituacaoDocumento(String uid, boolean situacao) {
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		List<Documento> documentoBd = documentoDAO.findByEntityUid(uid);
		
		
		
		if(documentoBd.isEmpty()){
			throw new DocumentoServiceException("Documento nao Existe");
		}
		
		if((documentoBd.get(0).getAtivo()) == situacao){
			throw new DocumentoServiceException("Situacao ja esta setada com esse valor");
		}
		
		Documento documento = documentoBd.get(0);
		documento.setAtivo(situacao);
		
		try {
			HibernateUtil.beginTransaction();
			documentoDAO.saveOrUpdate(documento);
			HibernateUtil.commitTransaction();
		} catch (Exception ex) {	
			ex.printStackTrace();
			HibernateUtil.rollbackTransaction();
			throw new UsuarioServiceException("Nao foi possivel setar a situacao");
		}finally{
			HibernateUtil.getSession().flush();
			HibernateUtil.getSession().clear();
		}
		
		return documento;
	}

	@Override
	/**
	 * Busca no banco de dados todos os arquivos ativos, consultados pela palavra chave
	 * e que obedecam a uma restricao.
	 * @param palavraChave - A palavra chave da pesquisa.
	 * @param restricao - A restricao que os documentos devem obedecer.
	 * @return Todos os documentos encontrados que contenham a palavra chave 
	 * no titulo, na descricao ou no autor.
	 */
	public List<Documento> listarDocumentosAtivos(String palavraChave,
			String restricao) {
		
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		List<Documento> documentosBd = new LinkedList<Documento>();
		
		if(restricao.equalsIgnoreCase("titulo")){
			return documentoDAO.findByTitleLike("%" + palavraChave + "%", true);
		}else if(restricao.equalsIgnoreCase("descricao")){
			return documentoDAO.findByDescriptionLike("%" + palavraChave + "%", true);
		}else if(restricao.equalsIgnoreCase("autor")){
			return documentoDAO.findByCreatorLike("%" + palavraChave + "%", true);
		}else if(restricao.equalsIgnoreCase("usuario")){
			UsuarioDAO usuarioDAO = HibernateDAOFactory.DEFAULT.buildUsuarioDAO();
			List<Usuario> usuariosBd = usuarioDAO.findByLogin(palavraChave);
			if(usuariosBd.isEmpty()){
				throw new UsuarioServiceException("Nao existe usuario com a palavra informada");
			}
			
			Usuario usuario = usuariosBd.get(0);
			return documentoDAO.findByUsuarioId(usuario.getId(), true);
		}
		
		return documentosBd;
	}

	@Override
	/**
	 * Busca o arquivo no banco de dados para o usuario realizar download.
	 * @param uid - O identificador do documento.
	 * @param diretorioAplicacao - O caminho de onde se encontra o arquivo.
	 * @return O nome do arquivo que sera feito download.
	 */
	public String requisitarArquivoParaDownload(String uid, String diretorioAplicacao) {
		// TODO Auto-generated method stub
		
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		List<Documento> documentosBd = new LinkedList<Documento>();
		
		documentosBd = documentoDAO.findByEntityUid(uid);
		
		if(documentosBd.isEmpty()){
			throw new DocumentoServiceException("Documento nao Existe");
		}
		
		Documento documento = documentosBd.get(0);
		
		
		
		Arquivo arquivo = documento.getArquivo();
		
		File diretorioUploads = new File(diretorioAplicacao + File.separator + "downloadbib");
		if(!diretorioUploads.exists()){
			diretorioUploads.mkdir();
		}
		
		File destino = new File(diretorioAplicacao + File.separator + "downloadbib"
				+ File.separator
				+ documento.getSource());
		
		gravaBytesFormFile(destino, arquivo.getConteudo());
		
		
		//Limpa a sessao
		HibernateUtil.getSession().clear();
		
		return documento.getSource();
	}

	@Override
	/**
	 * Apaga o arquivo da pasta temporaria de download que havia sido copiado.
	 * @param uid - Identificador do arquivo.
	 * @param diretorioAplicacao - Caminho da pasta temporaria.
	 * @return True se operacao foi realizada com sucesso, false caso contrario.
	 */
	public boolean limparArquivoRequisitado(String uid,
			String diretorioAplicacao) {
		
		documentoDAO = HibernateDAOFactory.DEFAULT.buildDocumentoDAO();
		List<Documento> documentosBd = new LinkedList<Documento>();
		
		documentosBd = documentoDAO.findByEntityUid(uid);
		
		if(documentosBd.isEmpty()){
			throw new DocumentoServiceException("Documento nao Existe");
		}
		
		Documento documento = documentosBd.get(0);
		
		
		
		
		
		File destino = new File(diretorioAplicacao + File.separator + "downloadbib"
				+ File.separator
				+ documento.getSource());
		
		if(destino.exists()){
			return destino.delete();
		}
		
		
		
		return false;
	}

}
